﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using Xunit.Sdk;

namespace System.Windows.Forms.Metafiles;

internal static partial class EmfValidator
{
    internal static void Validate(
        this EmfScope emf,
        DeviceContextState state,
        params IEmfValidator[] validationSteps)
    {
        ArgumentNullException.ThrowIfNull(state);
        ArgumentNullException.ThrowIfNull(validationSteps);

        int currentIndex = 0;
        IEmfValidator? currentValidator = validationSteps[currentIndex];

        emf.EnumerateWithState((ref EmfRecord record, DeviceContextState state) =>
            {
                if (currentValidator?.ShouldValidate(record.Type) ?? false)
                {
                    try
                    {
                        currentValidator.Validate(ref record, state, out bool complete);

                        if (complete)
                        {
                            // Current validator doesn't want to look at any more records.
                            currentIndex++;
                            currentValidator = currentIndex < validationSteps.Length
                                ? validationSteps[currentIndex]
                                : null;
                        }
                    }
                    catch (XunitException ex)
                    {
                        throw new WrappedXunitException(
                            $"\nValidator index {currentIndex}: {currentValidator!.GetType().Name} failed\n\n{emf.RecordsToString()}",
                            ex);
                    }
                }
                else
                {
                    Assert.False(IsRenderingRecord(record.Type), $"Got unexpected {record.Type}\n\n{emf.RecordsToString()}");
                }

                return true;
            },
            state);

        if (currentValidator is not null)
        {
            Assert.False(
                currentValidator.FailIfIncomplete,
                $"{currentValidator.GetType().Name} did not receive expected records\n\n{emf.RecordsToString()}");
        }
    }

    private static bool IsRenderingRecord(ENHANCED_METAFILE_RECORD_TYPE recordType)
        => recordType switch
        {
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIER => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYGON => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIERTO => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINETO => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYLINE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYGON => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_SETPIXELV => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ANGLEARC => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ELLIPSE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_RECTANGLE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ROUNDRECT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ARC => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_CHORD => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_PIE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTFLOODFILL => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_LINETO => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ARCTO => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYDRAW => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_CLOSEFIGURE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_FILLPATH => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEANDFILLPATH => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_STROKEPATH => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_FILLRGN => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_FRAMERGN => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_INVERTRGN => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_PAINTRGN => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_BITBLT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_STRETCHBLT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_MASKBLT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_PLGBLT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_SETDIBITSTODEVICE => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_STRETCHDIBITS => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTTEXTOUTA => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_EXTTEXTOUTW => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIER16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYGON16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINE16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYBEZIERTO16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYLINETO16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYLINE16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYPOLYGON16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYDRAW16 => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYTEXTOUTA => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_POLYTEXTOUTW => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_ALPHABLEND => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_TRANSPARENTBLT => true,
            ENHANCED_METAFILE_RECORD_TYPE.EMR_GRADIENTFILL => true,
            _ => false
        };
}
